using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Configuration;
using System.Xml;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.IO;
using System.Collections.Specialized;
using System.Reflection;

namespace sb.core {
    public partial class RewriteModule : IHttpModule {
        HttpContext Context;
        RewriteModuleSectionHandler cfg = null;

        public void Init(HttpApplication context) {
            cfg = (RewriteModuleSectionHandler)ConfigurationManager.GetSection("rewriteModule");

            context.BeginRequest += new EventHandler(RewriteModule_BeginRequest);
            context.PreRequestHandlerExecute += new EventHandler(RewriteModule_PreRequestHandlerExecute);
        }

        #region Core
        /*
         * if (!page.Exists && !default){
                url.aspx -> /default.aspx?langcode=en&pageuri=uri
         * }
        */

        private void RewriteModule_BeginRequest(object sender, EventArgs e) {
            Context = HttpContext.Current;
            if (cfg.RewriteOn && !IfResourcePhisicallyExists()) {
                XmlNode rules = cfg.XmlSection.SelectSingleNode("rewriteRules");
                string path = Context.Request.Path.ToLower();
                foreach (XmlNode xml in rules.SelectNodes("rule")) {
                    try {
                        BuildRewriteByRule(xml, path);
                    } catch (Exception ex) {
                        throw (new Exception("Incorrect rule. Path:" + path, ex));
                    }
                }
            }
        }

        private void BuildRewriteByRule(XmlNode xml, string path) {
            Regex rex = new Regex(xml.SelectSingleNode("source").InnerText, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            Match match = rex.Match(path);
            if (match.Success) {
                XmlAttribute rule = xml.Attributes["value"];

                if (rule != null) {
                    path = this.GetType().InvokeMember(rule.Value,
                        BindingFlags.NonPublic |
                        BindingFlags.Static |
                        BindingFlags.InvokeMethod, null, this, new object[] { rex, path }) as String;

                } else {
                    string dest = xml.SelectSingleNode("destination").InnerText;
                    if (dest != null) {
                        path = rex.Replace(path, dest);
                    }
                }

                if (!string.IsNullOrEmpty(path)) {
                    /* Persists the current querry string */
                    if (Context.Request.QueryString.Count != 0) {
                        string sign = (path.IndexOf('?') == -1) ? "?" : "&";
                        path = path + sign + Context.Request.QueryString.ToString();
                    }

                    /* Rewrites */
                    Context.Items.Add("OriginalUrl", Context.Request.RawUrl);
                    Context.RewritePath(cfg.RewriteBase + path);
                }
            }
        }
        void RewriteModule_PreRequestHandlerExecute(object sender, EventArgs e) {
            HttpApplication app = (HttpApplication)sender;
            if ((app.Context.CurrentHandler is Page) && app.Context.CurrentHandler != null) {
                Page pg = (Page)app.Context.CurrentHandler;
                pg.PreInit += new EventHandler(Page_PreInit);
            }
        }

        void Page_PreInit(object sender, EventArgs e) {
            // restore internal path to original
            // this is required to handle postbacks
            if (HttpContext.Current.Items.Contains("OriginalUrl")) {
                string path = (string)HttpContext.Current.Items["OriginalUrl"];

                // save query string parameters to context              
                RewriteContext con = new RewriteContext(HttpContext.Current.Request.QueryString, path);
                HttpContext.Current.Items["RewriteContextInfo"] = con;

                if (path.IndexOf("?") == -1) {
                    path += "?";
                }

                HttpContext.Current.RewritePath(path);
            }
        }
        #endregion

        #region Helper Routines
        private bool IfResourcePhisicallyExists() {
            return File.Exists(Context.Server.MapPath(Context.Request.Path.ToLower()));
        }
        public void Dispose() { }
        #endregion
    }
}